﻿using System;
using UnityEngine;
using UnityEngine.UIElements;
using Math = winS.Unity.Math;

namespace winS.UnityEditor.ProcessEditor
{
    /// <summary>
    /// 为 <see cref="StepGraphView"/> 提供移动相关功能的支持
    /// </summary>
    public class Mover : Manipulator
    {
        public static readonly float maxPanSpeed = 24f;
        public static readonly float minPanSpeed = 1f;
        public static readonly float panThresholdPercent = 0.1f;

        /// <summary>
        /// 当<see cref="StepGraphView"/> 进行边缘移动时, 调用该回调
        /// </summary>
        public event Action<Vector2> onEdgePan;

        private bool canMove;
        private bool enableEdgePan;

        private Vector2 panSpeed;
        private Vector2 startPoint;

        private readonly IVisualElementScheduledItem panSchedule;

        public Mover(StepGraphView stepGraphView) : base(stepGraphView)
        {
            stepGraphView.RegisterCallback<MouseUpEvent>(MouseUpCallback);
            stepGraphView.RegisterCallback<MouseDownEvent>(MouseDownCallback);
            stepGraphView.RegisterCallback<MouseMoveEvent>(MouseMoveCallback);

            panSchedule = stepGraphView.schedule.Execute(() => PanContentView(panSpeed)).Every(10).StartingIn(10);
            DisableEdgePan();
        }

        public void EnableEdgePan()
        {
            enableEdgePan = true;
            panSpeed = Vector2.zero;
            panSchedule.Resume();
        }
        public void DisableEdgePan()
        {
            enableEdgePan = false;
            panSchedule.Pause();
        }
        public void CalculatePanSpeed(Vector2 mousePosition)
        {
            Vector2 positionInGraphView = stepGraphView.WorldToLocal(mousePosition);
            Vector2 size = stepGraphView.layout.size;
            float xPanThreshold = size.x * panThresholdPercent;
            float yPanThreshold = size.y * panThresholdPercent;
            float xOverflow = Math.Overflow(positionInGraphView.x, xPanThreshold, size.x - xPanThreshold, xPanThreshold);
            float yOverflow = Math.Overflow(positionInGraphView.y, yPanThreshold, size.y - yPanThreshold, yPanThreshold);
            xOverflow = Math.Clamp(xOverflow, -1f, 1f);
            yOverflow = Math.Clamp(yOverflow, -1f, 1f);
            float xPanSpeed = xOverflow == 0f ? 0f : (Math.Abs(xOverflow) * (maxPanSpeed - minPanSpeed) + minPanSpeed) * Math.Sign(xOverflow);
            float yPanSpeed = yOverflow == 0f ? 0f : (Math.Abs(yOverflow) * (maxPanSpeed - minPanSpeed) + minPanSpeed) * Math.Sign(yOverflow);
            panSpeed = new Vector2(xPanSpeed, yPanSpeed);
        }

        private void PanContentView(Vector2 panSpeed)
        {
            if (panSpeed == Vector2.zero) return;
            viewTransform.position -= new Vector3(panSpeed.x, panSpeed.y, 0f);
            onEdgePan?.Invoke(panSpeed / viewTransform.scale.x);
        }

        private void MouseUpCallback(MouseUpEvent mouseUpEvent)
        {
            if (!canMove) return;
            stepGraphView.ReleaseMouse();
            canMove = false;
        }
        private void MouseDownCallback(MouseDownEvent mouseDownEvent)
        {
            int buttonID = mouseDownEvent.button;
            if (buttonID == 1) return;
            if (buttonID == 0) return;
            canMove = true;
            stepGraphView.CaptureMouse();
            startPoint = stepGraphView.ChangeCoordinatesTo(contentViewContainer, mouseDownEvent.localMousePosition);
            mouseDownEvent.StopImmediatePropagation();
        }
        private void MouseMoveCallback(MouseMoveEvent mouseMoveEvent)
        {
            if (enableEdgePan) CalculatePanSpeed(mouseMoveEvent.mousePosition);
            if (!canMove) return;
            Vector2 delta = stepGraphView.ChangeCoordinatesTo(contentViewContainer, mouseMoveEvent.localMousePosition) - startPoint;
            Vector3 scale = viewTransform.scale;
            viewTransform.position += new Vector3(delta.x * scale.x, delta.y * scale.y, 0f);
            mouseMoveEvent.StopPropagation();
        }
    }
}
